with Ada.Text_IO; use Ada.Text_IO;
package body Optimal is

   -----------
   -- creer --
   -----------

   function creer return Model is
   begin
      return Model'(tab_individus => null, nombre_posts => 0);
   end creer;

   -----------------
   -- est_inscrit --
   -----------------

   function est_inscrit (x : Model; n : individu) return boolean is
   begin
      if nombre_inscrits(x) = 0 then return false; else
         for i in 1 .. nombre_inscrits(x) loop
            if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
               return true;

            end if;
         end loop;
         return false;
      end if;
   end est_inscrit;

   ---------------------
   -- nombre_inscrits --
   ---------------------

   function nombre_inscrits (x : Model) return natural is
   begin
      if x.tab_individus = null then return 0; else
         return x.tab_individus.all'Length;
      end if;
   end nombre_inscrits;

   ------------------
   -- ieme_inscrit --
   ------------------

   function ieme_inscrit (x : Model; i : natural) return individu is
   begin
      return x.tab_individus(i).indiv.all;
   end ieme_inscrit;

   -------------
   -- bloquer --
   -------------

   function bloquer (x : Model; m : individu; n : individu) return Model is
   begin
      declare
         individu_class_bloque : individu_class;
         modelReturn : Model := x;
      begin
         for i in 1 .. modelReturn.tab_individus.all'Length loop
            if modelReturn.tab_individus(i).indiv.all <= n and n <= modelReturn.tab_individus(i).indiv.all then
               individu_class_bloque := modelReturn.tab_individus(i).all;
            end if;
         end loop;

         for i in 1 .. modelReturn.tab_individus.all'Length loop
            if modelReturn.tab_individus(i).indiv.all <= m and m <= modelReturn.tab_individus(i).indiv.all then
               if modelReturn.tab_individus(i).listeBloques /= null then
                  declare
                     liste_bloques : individus(1 .. modelReturn.tab_individus(i).listeBloques.all'Length + 1);
                  begin
                     liste_bloques(1 .. modelReturn.tab_individus(i).listeBloques.all'Length) := modelReturn.tab_individus(i).listeBloques.all;
                     liste_bloques(liste_bloques'Last) := new individu_class'(individu_class_bloque);
                     modelReturn.tab_individus(i).listeBloques := new individus'(liste_bloques);
                  end;
               else
                  declare
                     liste_bloques : individus(1 ..1);
                  begin
                     liste_bloques(liste_bloques'First) := new individu_class'(individu_class_bloque);
                     modelReturn.tab_individus(i).listeBloques := new individus'(liste_bloques);
                  end;
               end if;
               if not (modelReturn.tab_individus(i).listeCercles = null) then
                  declare
                    -- newModel : Model := modelReturn;
                  begin
                     for j in 1 .. modelReturn.tab_individus(i).listeCercles.all'Length loop
                        if est_membre_de(x => modelReturn,
                                         m => m,
                                         c => modelReturn.tab_individus(i).listeCercles(j).cerc.all,
                                         n => n) then
                           modelReturn := retirer_membre(x => modelReturn,
                                                      n => m,
                                                      c => modelReturn.tab_individus(i).listeCercles(j).cerc.all,
                                                      p => n);
                        end if;
                     end loop;
                     -- modelReturn := newModel;
                  end;
               end if;
            end if;
         end loop;
         return Model'(modelReturn);
      end;
   end bloquer;

   ---------------
   -- debloquer --
   ---------------

   function debloquer (x : Model; m : individu; n : individu) return Model is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= m and m <= x.tab_individus(i).indiv.all then
            declare
               liste_bloques : individus(1 .. x.tab_individus(i).listeBloques.all'Length - 1);
               index_bloque : natural := 1;
            begin
               for j in 1 .. x.tab_individus(i).listeBloques.all'Length loop
                  if not (x.tab_individus(i).listeBloques(j).indiv.all <= n and
                            n <= x.tab_individus(i).listeBloques(j).indiv.all) then
                     liste_bloques(index_bloque) := new individu_class'(x.tab_individus(i).listeBloques(j).all);
                     index_bloque := index_bloque + 1;
                  end if;
               end loop;
               x.tab_individus(i).listeBloques := new individus'(liste_bloques);
            end;
         end if;
      end loop;
      return Model'(tab_individus => x.tab_individus, nombre_posts => x.nombre_posts);
   end debloquer;

   ----------------
   -- est_bloque --
   ----------------

   function est_bloque
     (x : Model;
      m : individu;
      n : individu)
      return boolean
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= m and m <= x.tab_individus(i).indiv.all then
            if x.tab_individus(i).listeBloques = null then return false; else
               for j in 1 .. x.tab_individus(i).listeBloques.all'Length loop
                  if x.tab_individus(i).listeBloques(j).indiv.all <= n and
                    n <= x.tab_individus(i).listeBloques(j).indiv.all then
                     return true;
                  end if;
               end loop;
            end if;
         end if;
      end loop;
      return false;
   end est_bloque;

   -------------------
   -- est_cercle_de --
   -------------------

   function est_cercle_de
     (x : Model;
      n : individu;
      c : cercle)
      return boolean
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            if x.tab_individus(i).listeCercles = null then return false; else
               for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
                  if x.tab_individus(i).listeCercles(j).cerc.all <= c
                    and c <= x.tab_individus(i).listeCercles(j).cerc.all then
                     return true;
                  end if;
               end loop;
            end if;
         end if;
      end loop;
      return false;
   end est_cercle_de;

   --------------------
   -- retirer_cercle --
   --------------------

   function retirer_cercle
     (x : Model;
      n : individu;
      c : cercle)
      return Model
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            declare
               listeCercles : cercles(1 .. x.tab_individus(i).listeCercles.all'Length - 1);
               indexCercles : natural := 1;
            begin
               for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
                  if not (x.tab_individus(i).listeCercles(j).cerc.all <= c
                          and c <= x.tab_individus(i).listeCercles(j).cerc.all) then
                     listeCercles(indexCercles) := new cercle_class'(x.tab_individus(i).listeCercles(j).all);
                     indexCercles := indexCercles + 1;
                  end if;
               end loop;
               x.tab_individus(i).listeCercles := new cercles'(listeCercles);
            end;
         end if;
      end loop;
      return Model'(tab_individus => x.tab_individus, nombre_posts => x.nombre_posts);
   end retirer_cercle;

   -----------------------
   -- nombre_cercles_de --
   -----------------------

   function nombre_cercles_de (x : Model; n : individu) return natural is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            if x.tab_individus(i).listeCercles = null then return 0; else
               return x.tab_individus(i).listeCercles.all'Length;
            end if;
         end if;
      end loop;
      return 0;
   end nombre_cercles_de;

   -----------------
   -- ieme_cercle --
   -----------------

   function ieme_cercle
     (x : Model;
      n : individu;
      i : natural)
      return cercle
   is
   begin
      for j in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(j).indiv.all <= n and n <= x.tab_individus(j).indiv.all then
            return x.tab_individus(j).listeCercles(i).cerc.all;
         end if;
      end loop;
      raise Program_Error;
   end ieme_cercle;

   -------------------
   -- est_membre_de --
   -------------------

   function est_membre_de
     (x : Model;
      m : individu;
      c : cercle;
      n : individu)
      return boolean
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= m and m <= x.tab_individus(i).indiv.all then
            for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
               if x.tab_individus(i).listeCercles(j).cerc.all <= c and
                 c <= x.tab_individus(i).listeCercles(j).cerc.all then
                  if x.tab_individus(i).listeCercles(j).listeMembres = null then return false; else
                     for k in 1 .. x.tab_individus(i).listeCercles(j).listeMembres.all'Length loop
                        if x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all <= n and
                          n <= x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all then
                           return true;
                        end if;
                     end loop;
                  end if;
               end if;
            end loop;
         end if;
      end loop;
      return false;
   end est_membre_de;

   --------------------
   -- retirer_membre --
   --------------------

   function retirer_membre
     (x : Model;
      n : individu;
      c : cercle;
      p : individu)
      return Model
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
               if x.tab_individus(i).listeCercles(j).cerc.all <= c and
                 c <= x.tab_individus(i).listeCercles(j).cerc.all then
                  declare
                     listeMembres : individus(1 .. x.tab_individus(i).listeCercles(j).listeMembres.all'Length - 1);
                     indexMembres : natural := 1;
                  begin
                     for k in 1 .. x.tab_individus(i).listeCercles(j).listeMembres.all'Length loop
                        if not (x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all <= p and
                                  p <= x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all) then
                           listeMembres(indexMembres) := x.tab_individus(i).listeCercles(j).listeMembres(k);
                        end if;
                     end loop;
                     x.tab_individus(i).listeCercles(j).listeMembres := new individus'(listeMembres);
                  end;
               end if;
            end loop;
         end if;
      end loop;
      return Model'(tab_individus => x.tab_individus, nombre_posts => x.nombre_posts);
   end retirer_membre;

   -----------------------
   -- nombre_membres_de --
   -----------------------

   function nombre_membres_de
     (x : Model;
      n : individu;
      c : cercle)
      return natural
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            if x.tab_individus(i).listeCercles = null then return 0; else
               for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
                  if x.tab_individus(i).listeCercles(j).cerc.all <= c and
                    c <= x.tab_individus(i).listeCercles(j).cerc.all then
                     return x.tab_individus(i).listeCercles(j).listeMembres.all'Length;
                  end if;
               end loop;
            end if;
         end if;
      end loop;
      return 0;
   end nombre_membres_de;

   -----------------
   -- ieme_membre --
   -----------------

   function ieme_membre
     (x : Model;
      n : individu;
      c : cercle;
      i : natural)
      return individu
   is
   begin
      for k in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(k).indiv.all <= n and n <= x.tab_individus(k).indiv.all then
            for j in 1 .. x.tab_individus(k).listeCercles.all'Length loop
               if x.tab_individus(k).listeCercles(j).cerc.all <= c and
                 c <= x.tab_individus(k).listeCercles(j).cerc.all then
                  return x.tab_individus(k).listeCercles(j).listeMembres(i).indiv.all;
               end if;
            end loop;
         end if;
      end loop;
      raise Program_Error;
   end ieme_membre;

   ------------
   -- poster --
   ------------

   function poster
     (x : Model;
      n : individu;
      c : cercle;
      t : texte)
      return Model
   is
   begin
      declare
         idPost : natural := nombre_posts(x) + 1;
      begin
         for i in 1 .. x.tab_individus.all'Length loop
            if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
               x.tab_individus(i).mur_individu.all :=
                 afficher(x.tab_individus(i).mur_individu.all, n, t, idPost);
               for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
                  if x.tab_individus(i).listeCercles(j).cerc.all <= c and
                    c <= x.tab_individus(i).listeCercles(j).cerc.all then
                     for k in 1 .. x.tab_individus(i).listeCercles(j).listeMembres.all'Length loop
                        if not est_bloque(x, x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all
                                          ,n) then
                           for l in 1 .. x.tab_individus.all'Length loop
                              if x.tab_individus(l).indiv.all <= x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all
                                and x.tab_individus(i).listeCercles(j).listeMembres(k).indiv.all <= x.tab_individus(l).indiv.all then

                                 put_line("Salut");
                                 x.tab_individus(l).mur_individu.all :=
                                   afficher(x.tab_individus(l).mur_individu.all
                                            ,n, t, idPost);
                              end if;
                           end loop;
                        end if;
                     end loop;
                  end if;
               end loop;
            end if;
         end loop;
      end;
      return Model'(tab_individus => x.tab_individus, nombre_posts => x.nombre_posts + 1);
   end poster;

   ---------------
   -- commenter --
   ---------------

   function commenter
     (x : Model;
      n : individu;
      i : natural;
      t : texte)
      return Model
   is
   begin
      for l in 1 .. x.tab_individus.all'Length loop
         if est_defini(mu => consulter(x, x.tab_individus(l).indiv.all),
                       i  => i) then
            x.tab_individus(l).mur_individu.all := afficher(mu => x.tab_individus(i).mur_individu.all,
                                                            m  => n,
                                                            t  => t,
                                                            i  => i);
         end if;
      end loop;
      return Model'(tab_individus => x.tab_individus, nombre_posts => x.nombre_posts);
   end commenter;

   ------------------
   -- nombre_posts --
   ------------------

   function nombre_posts (x : Model) return natural is
   begin
      return x.nombre_posts;
   end nombre_posts;

   ---------------
   -- consulter --
   ---------------

   function consulter (x : Model; n : individu) return mur is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            return x.tab_individus(i).mur_individu.all;
         end if;
      end loop;
      raise Program_Error;
   end consulter;

   --------------------
   -- ajouter_membre --
   --------------------

   function ajouter_membre
     (x : Model;
      n : individu;
      c : cercle;
      p : individu)
      return Model
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            for j in 1 .. x.tab_individus(i).listeCercles.all'Length loop
               if x.tab_individus(i).listeCercles(j).cerc.all <= c and c <= x.tab_individus(i).listeCercles(j).cerc.all then
                  if x.tab_individus(i).listeCercles(j).listeMembres = null then
                     declare
                        listeMembres : individus(1 ..1);
                     begin
                        listeMembres(listeMembres'First) := new individu_class'(indiv => new individu'(p),
                                                                                listeCercles => null,
                                                                                listeBloques => null,
                                                                                mur_individu => new mur'(creer(p)));
                        x.tab_individus(i).listeCercles(j).listeMembres := new individus'(listeMembres);
                     end;
                  else

                     declare
                        listeMembres : individus(1 .. x.tab_individus(i).listeCercles(j).listeMembres.all'Length + 1);
                        indexMembre : natural := listeMembres'Length;
                        dejaAjoute : boolean := false;
                     begin
                        while indexMembre > 1 loop
                           if p <= x.tab_individus(i).listeCercles(j).listeMembres(indexMembre - 1).indiv.all then
                              listeMembres(indexMembre) :=  new individu_class'(x.tab_individus(i).listeCercles(j).listeMembres(indexMembre - 1).all);
                           elsif not dejaAjoute then
                              listeMembres(indexMembre) := new individu_class'(indiv => new individu'(p),
                                                                               listeCercles => null,
                                                                               listeBloques => null,
                                                                               mur_individu => new mur'(creer(p)));
                              dejaAjoute := true;
                           else
                              listeMembres(indexMembre) := new individu_class'(x.tab_individus(i).listeCercles(j).listeMembres(indexMembre).all);
                           end if;
                           indexMembre := indexMembre - 1;
                        end loop;
                        if not dejaAjoute then
                           listeMembres(listeMembres'First) := new individu_class'(indiv => new individu'(p),
                                                                                   listeCercles => null,
                                                                                   listeBloques => null,
                                                                                   mur_individu => new mur'(creer(p)));
                        else
                           listeMembres(listeMembres'First) := new individu_class'(
                                                                                   x.tab_individus(i).listeCercles(j).listeMembres(x.tab_individus(i).listeCercles(j).listeMembres.all'First).all);
                        end if;
                        x.tab_individus(i).listeCercles(j).listeMembres := new individus'(listeMembres);
                     end;
                  end if;
               end if;
            end loop;
         end if;
      end loop;
      return model'(tab_individus => x.tab_individus,
                    nombre_posts  => x.nombre_posts);
   end ajouter_membre;

   --------------------
   -- ajouter_cercle --
   --------------------

   function ajouter_cercle
     (x : Model;
      n : individu;
      c : cercle)
      return Model
   is
   begin
      for i in 1 .. x.tab_individus.all'Length loop
         if x.tab_individus(i).indiv.all <= n and n <= x.tab_individus(i).indiv.all then
            if x.tab_individus(i).listeCercles /= null then
               declare
                  listeCercles : cercles(1 .. x.tab_individus(i).listeCercles.all'Length + 1);
                  indexCercle : natural := listeCercles'Length;
                  dejaAjoute : boolean := false;
               begin
                  while indexCercle > 1 loop
                     if c <= x.tab_individus(i).listeCercles(indexCercle - 1).all.cerc.all then
                        listeCercles(indexCercle) :=  new cercle_class'(x.tab_individus(i).listeCercles(indexCercle - 1).all);
                     elsif not dejaAjoute then
                        listeCercles(indexCercle) := new cercle_class'(cerc => new cercle'(c),
                                                                       proprietaire => new individu'(n),
                                                                       listeMembres => null);
                        dejaAjoute := true;
                     else
                        listeCercles(indexCercle) := new cercle_class'(x.tab_individus(i).listeCercles(indexCercle).all);
                     end if;
                     indexCercle := indexCercle - 1;
                  end loop;
                  if not dejaAjoute then
                     listeCercles(listeCercles'First) := new cercle_class'(cerc => new cercle'(c),
                                                                           proprietaire => new individu'(n),
                                                                           listeMembres => null);
                  else
                     listeCercles(listeCercles'First) :=
                       new cercle_class'(x.tab_individus(i).listeCercles(x.tab_individus(i).listeCercles.all'First).all);
                  end if;
                  x.tab_individus(i).listeCercles := new cercles'(listeCercles);
               end;
            else
               declare
                  listeCercles : cercles(1 .. 1);
               begin
                  listeCercles(listeCercles'First) := new cercle_class'(cerc => new cercle'(c),
                                                                        proprietaire => new individu'(n),
                                                                        listeMembres => null);
                  x.tab_individus(i).listeCercles := new cercles'(listeCercles);
               end;
            end if;
         end if;
      end loop;
      return model'(tab_individus => x.tab_individus,
                    nombre_posts  => x.nombre_posts);
   end ajouter_cercle;

   --------------
   -- inscrire --
   --------------

   function inscrire (x : Model; n : individu) return Model is
   begin
      if x.tab_individus = null then
         declare
            listeIndividus : individus(1 .. 1);
         begin

            listeIndividus(listeIndividus'First) := new individu_class'(indiv => new individu'(n),
                                                                        listeCercles => null,
                                                                        listeBloques => null,
                                                                        mur_individu => new mur'(creer(n)));
            --x.tab_individus.all := individus'(listeIndividus);
            return model'(tab_individus => new individus'(listeIndividus),
                          nombre_posts  => x.nombre_posts);
         end;
      else

         declare
            listeIndividus : individus(1 .. x.tab_individus.all'Length + 1);
            indexIndividu : natural := listeIndividus'Length;
            dejaAjoute : boolean := false;
         begin
            while indexIndividu > 1 loop
               if n <= x.tab_individus(indexIndividu - 1).all.indiv.all then
                  listeIndividus(indexIndividu) :=  x.tab_individus(indexIndividu - 1);
               elsif not dejaAjoute then
                  listeIndividus(indexIndividu) := new individu_class'(indiv        => new individu'(n),
                                                                       listeCercles => null,
                                                                       listeBloques => null,
                                                                       mur_individu => new mur'(creer(n)));
                  dejaAjoute := true;
               else
                  listeIndividus(indexIndividu) := new individu_class'(x.tab_individus(indexIndividu).all);
               end if;
               indexIndividu := indexIndividu - 1;
            end loop;
            if not dejaAjoute then
               listeIndividus(listeIndividus'First) := new individu_class'(indiv        => new individu'(n),
                                                                           listeCercles => null,
                                                                           listeBloques => null,
                                                                           mur_individu => new mur'(creer(n)));
            else
               listeIndividus(listeIndividus'First) :=
                 new individu_class'(x.tab_individus(x.tab_individus.all'First).all);
            end if;
            --x.tab_individus.all := individus'(listeIndividus);
            return model'(tab_individus => new individus'(listeIndividus),
                          nombre_posts  => x.nombre_posts);
         end;
      end if;

   end inscrire;

end Optimal;
